/*-----------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in
compliance with the License.You may obtain a copy of the License at
http://www.mozilla.org/MPL/MPL-1.1.html
	
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.
	
The Original Code is Fever RS ( ActionScript 3 ) Framework code.
	
The Initial Developer of the Original Code is Romain Ecarnot.
Portions created by Initial Developer are Copyright (C) 2006
the Initial Developer. All Rights Reserved.
	
Contributor(s):
	
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-----------------------------------------------------------------------------*/

package fever.utils 
{

	/**
	 * String methods
	 * 
	 * @author Romain Ecarnot
	 */
	final public class StringUtil 
	{
		//--------------------------------------------------------------------
		// Constants
		//--------------------------------------------------------------------
		
		/** Defines default empty characters values */
		public static const EMPTY_CHARS : String = "\n\t\r ";
		
		
		//--------------------------------------------------------------------
		// Public API
		//--------------------------------------------------------------------
		
		/**
		 * Replaced character defined at <code>index</code> position in 
		 * <code>source</code> string with passed-in <code>replaceWith</code> 
		 * substring.
		 * 
		 * @param source String to parse
		 * @param index Position
		 * @param replaceWith String to insert
		 */
		public static function replaceAt( source : String, index : Number, 
			replaceWith : String ) : String
		{
			var a : Array = source.split( "" );
			a.splice( index, 1, replaceWith );
			return a.join( "" );
		}

		/**
		 * Inserts passed-in <code>insert</code> string at <code>index</code> 
		 * position in <code>source</code> string.
		 * 
		 * @param source String to parse
		 * @param index Position
		 * @param insert String to insert in source string
		 */
		public static function insertAt( source : String, index : int, 
			insert : String ) : String
		{
			var a : Array = source.split( "" );
			a.splice( index, 0, insert );
			return a.join( '' );
		}

		/**
		 * Removes passed-in <code>remvove</code> substring from 
		 * <code>source</code> string.
		 * 
		 * @param source String to parse
		 * @param remove String to remove
		 */
		public static function remove( source : String, 
			toRemove : String ) : String 
		{
			return source.replace( toRemove, "" );
		}

		/**
		 * Removes character at passed-in <code>index</code> position.
		 * 
		 * @param source String to parse
		 * @param index Position
		 */
		public static function removeAt( source : String, 
			index : int ) : String
		{
			var a : Array = source.split( "" );
			a.splice( index, 1 );
			source = a.join( '' );
			return source;
		}

		/**
		 * Reverses the passed-in <code>source</code> string.
		 * 
		 * @param source source string
		 */
		public static function reverse( source : String ) : String
		{
			var a : Array = source.split( "" );
			a.reverse( );
			return a.join( '' );
		}

		/**
		 * Removes all empty characters, defined in 
		 * <code>StringUtil.EMPTY_CHARS</code> property, at the beginning 
		 * <strong>and</strong> end of the passed-in 
		 * <code>source</code> string.
		 * 
		 * @param source Source string
		 * @param chars Character list to trim
		 */
		public static function trim( source : String, chars : String = EMPTY_CHARS ) : String
		{
			return leftTrim( rightTrim( source, chars ), chars );
		}
		
		/**
		 * Removes all empty characters, defined in <code>StringUtil.EMPTY_CHARS</code> 
		 * property, at the beginning of the passed-in <code>source</code> 
		 * string.
		 * 
		 * @param source Source string
		 * @param chars Character list to trim. 
		 */
		public static function leftTrim( source : String, 
			chars : String = EMPTY_CHARS ) : String
		{
			return _leftTrimForChars( source, chars	);
		}

		/**
		 * Removes all empty characters, defined in <code>StringUtil.EMPTY_CHARS</code> 
		 * property, at the end of the passed-in <code>source</code> string.
		 * 
		 * @param source Source string
		 * @param chars Character list to trim.
		 */
		public static function rightTrim( source : String, 
			chars : String = EMPTY_CHARS ) : String
		{
			return _rightTrimForChars( source, chars );
		}

		/**
		 * Returns <code>true</code> if  passed-in <code>source</code> 
		 * starts with passed-in <code>search</code> string.
		 * 
		 * @param source string to evaluate
		 * @param search search string
		 */
		public static function isStartingWith( source : String, 
			search : String ) : Boolean
		{
			if ( source.indexOf( search ) == 0) return true;
			return false;
		}

		/**
		 * Returns <code>true</code> if passed-in <code>source</code> ends 
		 * with passed-in <code>search</code> string.
		 *
		 * @param source string to evaluate
		 * @param search search string
		 */
		public static function isEndingWith( source : String, 
			search : String ) : Boolean
		{
			if (source.lastIndexOf( search ) == (source.length - search.length)) 
				return true;
			return false;
		}

		/**
		 * Returns <code>true</code> if passed-in <code>search</code> string 
		 * is contained on passed-in <code>source</code> one.
		 */
		public static function contains( source : String, 
			search : String ) : Boolean
		{
			return ( source.indexOf( search ) != -1 );
		}

		/**
		 * Returns <code>true</code> is 2 passed string are equals.
		 * 
		 * <p>Use passed-in <code>caseSensitive</code> argument to use 
		 * case sensitive test.</p>
		 * 
		 * @param source1 First string
		 * @param source2 Second string to compare
		 * @param caseSensitive (optional) <code>true</code> to enable case 
		 * sensitive test, otherwise <code>false</code>
		 */
		public static function areEqual( source1 : String, source2 : String, 
			caseSensitive : Boolean = false ) : Boolean
		{
			if ( caseSensitive ) return ( source1 == source2 );
			else return ( source1.toUpperCase( ) == source2.toUpperCase( ) );
		}

		/**
		 * Returns true if the specified string is a single space, 
		 * tab, return, or newline character.
		 * 
		 * @param char String to test
		 */
		public static function isWhiteSpace( char : String ) : Boolean
		{
			if( EMPTY_CHARS.indexOf( char.charAt( 0 ) ) != -1 ) return true;
			return false;
		}

		/**
		 * Substitutes "{n}" tokens within the specified string with 
		 * their respective arguments passed in.
		 * 
		 * @param source String to parse
		 * @param rest to use for substitution.
		 */
		public static function substitute( source : String, 
			...rest ) : String
		{
			var l : int = rest.length;
			
			for( var i : int = 0; i < l ; i += 1 )
			{
				source = source.replace( "{" + i + "}", rest[i] );
			}
			
			return source;
		}

		/**
		 * Substitutes "{n}" tokens within the specified string with 
		 * passed-in <code>array</code> collection.
		 * 
		 * @param source String to parse
		 * @param array Content to use for substitution.
		 */
		public static function substituteByArray( source : String, 
			array : Array ) : String
		{
			var l : int = array.length;
			
			for( var i : int = 0; i < l ; i += 1 )
			{
				source = source.replace( "{" + i + "}", array[i] );
			}
			return source;
		}

		/**
		 * Capitalizes the first letter of passed-in 
		 * <code>source</code> string.
		 */
		public static function uppercaseFirst( source : String ) : String
		{
			return source.charAt( 0 ).toUpperCase( ) + source.substr( 1 );
		}

		/**
		 * Capitalizes the first character of every word in 
		 * the passed-in <code>source</code>.
		 */
		public static function uppercaseWords( source : String ) : String
		{
			var chars : Array = source.split( ' ' );
			var l : Number = chars.length;
			
			for ( var i : Number = 0; i < l ; i++ )
			{
				chars[i] = StringUtil.uppercaseFirst( chars[i] );
			}
			return chars.join( ' ' );
		}

		/**
		 * Multiply passed-in <code>source</code>, <code>n</code> times.
		 */
		public static function multiply( source : String,  n : Number) : String
		{
			var ret : String = "";
			for( var i : Number = 0; i < n ; i++ ) ret += source;
			return ret;
		}

		/**
		 * Forces the passed-in <code>source</code> to starts with 
		 * <code>start</code> character.
		 * 
		 * <p>Use <code>check</code> parameter to 'leftTrim' the source before.
		 */
		public static function startWithChar( source : String, start : String, check : Boolean ) : String
		{
			if( start.length > 1 ) return source;
			
			var tmp : String = ( check ) 
				? StringUtil.leftTrim( source, EMPTY_CHARS + start ) 
				: source;
			
			return ( start + tmp ); 
		}

		/**
		 * Forces the passed-in <code>source</code> to finish with 
		 * <code>end</code> character.
		 * 
		 * <p>Use <code>check</code> parameter to 'rightTrim' the source before.
		 */
		public static function finishWithChar( source : String, end : String, check : Boolean ) : String
		{
			if( end.length > 1 ) return source;
			
			var tmp : String = ( check ) 
				? StringUtil.rightTrim( source, EMPTY_CHARS + end ) 
				: source;
			
			return ( tmp + end ); 
		}

		/**
		 * Summarizes passed <code>source</code> using <code>charLength</code> chars length limit.
		 * 
		 * <p>This method use <strong>character</strong> to build the summary.
		 * 
		 * @param source Source string
		 * @param charLength Chars count limit
		 * @param removeLineBreak <code>true</code> to remove all break line 
		 * in <code>source</code> string<br> ( default is <code>false</code> ) 
		 */
		public static function summarise( source : String, charLength : Number, 
			removeLineBreak : Boolean = false ) : String
		{
			if( charLength > 0 )
			{
				source = StringUtil.trim( source );
				if( removeLineBreak ) source = source.replace( "\n", "" );
				
				var ex : Array = source.split( ' ' );
				var tL : Number = ex.length;
				var char : String = '';
				
				for( var i : Number = 0; i < tL ; i++ )
				{
					var sSub : String = ex[ i ];
					var aSub : Array = sSub.split( "" );
					var subL : Number = aSub.length;
					
					if( char.length + subL <= charLength )
						char += ( ' ' + sSub );
					else break;
				}
				
				char = StringUtil.leftTrim( char ) + "...";
				
				return char;	
			}
			else return source;
		}

		/**
		 * Summarizes passed <code>source</code> using <code>wordLength</code> words length limit.
		 * 
		 * <p>This method use <strong>word</strong> to build the summary.</p>
		 * 
		 * @param source Source string
		 * @param wordLength Words count limit
		 * @param removeLineBreak <code>true</code> to remove all break line 
		 * in <code>source</code> string<br> ( default is <code>false</code> ) 
		 */
		public static function summariseWord( source : String, 
			wordLength : Number, removeLineBreak : Boolean = false ) : String
		{
			if( wordLength > 0 )
			{
				source = StringUtil.trim( source );
				if( removeLineBreak ) source = source.replace( "\n", "" );
				
				return source.split( ' ' ).slice( 0, wordLength ).concat( "..." ).join( " " );
			}
			else return source;
		}
		
		/**
		 * Returns <code>true</code> if <code>source</code> is a valid string.
		 */
		public static function isValid( source : String) : Boolean
		{
			return source.length > -1;
		}
		
		/**
		 * @example
		 * <listing version="3.0>
		 * 
		 * var n : Number = 6;
		 * var result : String = StringUtil.formatNumber( n, 3, '0' );
		 * 
		 * //result == 006
		 * </listing>
		 */
		public static function formatNumber( num : Number, l : Number, 
			char : String, right : Boolean ) : String
		{
			var nDiff : Number = l - num.toString( ).length;
			var sReste : String = "";
			
			if( !char ) char = " ";
			
			if( nDiff > 0 )
			{
				for( var i : Number = 0; i < nDiff ; i++ ) sReste += char;
			}
			return ( right ) ? num + sReste : sReste + num;
		}
		
		
		//--------------------------------------------------------------------
		// Private implementations
		//--------------------------------------------------------------------
		
		/** @private */
		public function StringUtil( access : PrivateConstructorAccess ) 
		{
		}
		
		/** @private */
		private static function _leftTrimForChars( source : String, escape : String ) : String 
		{
			var i : int = 0;
			var l : int = source.length;
			while (i < l && escape.indexOf( source.charAt( i ) ) >= 0) i++;
			return (i > 0 ? source.substr( i, l ) : source);
		}
		
		/** @private */
		private static function _rightTrimForChars( source : String, escape : String ) : String 
		{
			var i : int = 0;
			var l : int = source.length - 1;
			while (i < l && escape.indexOf( source.charAt( l ) ) >= 0) l--;
			return (l >= 0 ? source.substr( i, l + 1 ) : source);
		}
	}
}


internal class PrivateConstructorAccess 
{
}
